/**
 * 实现移动对象
 * 创建者：Carburn Ashroom
 * 2022.12.14
 */

#include "move_object.h"

namespace Object {

    default_random_engine Enemy::eng {static_cast<uint32_t>(time(nullptr))};

    void Move_object::draw_this(QPixmap* pix) const
    {
        QPainter painter {pix};
        painter.drawPixmap(rect.topLeft(), pixmap);
    }

    bool Move_object::auto_destroy() const
    {
        if (rect.y() <= -rect.height())
            return true;
        if (rect.y() >= screen.height())
            return true;
        return false;
    }

    void Move_object::set_pixmap(QString pix_path)
    {
        pixmap.load(pix_path);
        rect.setSize(pixmap.size());
    }

    bool Bullet::update_pos()
    {
        offset_move(get_speed());
        return auto_destroy();
    }

    bool Live_object::destroy_collision(QList<Bullet>& bullets, QList<Bomb::Bomb>& bombs) const
    {
        for (auto i=bullets.begin(); i!=bullets.end(); ++i)
            if (collision(*i)) {
                Bomb::Bomb new_bomb {(*i).get_rect().center()};
                bullets.erase(i);
                bombs.append(new_bomb);
                return true;
            }
        return auto_destroy();
    }

    bool Live_object::interval()
    {
        if (shoot_interval-- == 0) {
            shoot_interval = init_interval;
            return true;
        }
        return false;
    }

    void Live_object::set_interval(int itv)
    {
        init_interval = itv;
        shoot_interval = itv;
    }

    void Player::move_up()
    {
        offset_move(get_speed());
        if (get_rect().y() < 0)
            move(QPoint{get_rect().x(),0});
    }

    void Player::move_down()
    {
        offset_move(-get_speed());
        int new_y;
        if (get_rect().y() > (new_y=get_screen().height()-get_rect().height()))
            move(QPoint{get_rect().x(),new_y});
    }

    void Player::move_right()
    {
        QPoint pos {get_rect().topLeft()};
        pos += QPoint{get_speed(),0};
        int new_x;
        if (pos.x() > (new_x=get_screen().width()-get_rect().width()))
            pos.setX(new_x);
        move(pos);
    }

    void Player::move_left()
    {
        QPoint pos {get_rect().topLeft()};
        pos -= {get_speed(),0};
        if (pos.x() < 0)
            pos.setX(0);
        move(pos);
    }

    void Player::shoot(QList<Bullet>& bullets)
    {
        if (interval()) {
            double gap {get_rect().width()/(player_cols+1.)};
            Bullet mobel_bullet {bullet_player,bullet_ply_spd,get_screen()};
            QPoint bullet_pos {get_rect().topLeft()};
            for (int i{}; i!=player_cols; ) {
                Bullet new_bullet {mobel_bullet};
                new_bullet.move_center(bullet_pos+QPoint{static_cast<int>(gap*++i),0});
                bullets.append(new_bullet);
            }
        }
    }

    void Player::draw_this(QPixmap *pix) const
    {
        Move_object::draw_this(pix);
        if (keyboard) {
            QPainter painter {pix};
            painter.setBrush(QBrush{player_brush});
            painter.drawEllipse(get_rect().center(), player_point_r, player_point_r);
        }
    }

    bool Player::collision(QList<Enemy>& enemys, QList<Bullet>& bullets, QList<Bomb::Bomb>& bombs) const
    {
        if (keyboard) {
            QRect point {center_point()};
            for (auto i=bullets.begin(); i!=bullets.end(); ++i)
                if ((*i).get_rect().intersects(point)) {
                    bullets.erase(i);
                    Bomb::Bomb temp_bomb {point.center()};
                    bombs.append(temp_bomb);
                    return true;
                }
            for (auto i=enemys.begin(); i!=enemys.end(); ++i)
                if ((*i).get_rect().intersects(point)) {
                    enemys.erase(i);
                    Bomb::Bomb new_bomb {point.center()};
                    bombs.append(new_bomb);
                    return true;
                }
            return false;
        }
        else
            if (destroy_collision(bullets, bombs))
                return true;
            else
                return enemy_collision(enemys, bombs);
    }

    bool Player::collision(QRect rect) const
    {
        if (!keyboard)
            return get_rect().intersects(rect);
        else
            return center_point().intersects(rect);
    }

    QRect Player::center_point() const
    {
        QRect point {0,0,player_point_r,player_point_r};
        point.moveCenter(get_rect().center());
        return point;
    }

    void Player::mouse_move(QPoint pos)
    {
        if (!keyboard)
            move_center(pos);
    }

    void Player::move_center(QPoint pos)
    {
        Move_object::move_center(pos);
        QPoint now_pos {get_rect().topLeft()};
        if (now_pos.x() < 0)
            now_pos.setX(0);
        if (now_pos.y() < 0)
            now_pos.setY(0);
        if (get_rect().bottom() > get_screen().height())
            now_pos.setY(get_screen().height()-get_rect().height());
        if (get_rect().right() > get_screen().width())
            now_pos.setX(get_screen().width()-get_rect().width());
        move(now_pos);
    }

    void Player::init_pos()
    {
        QPoint center {get_screen().center()};
        center += QPoint{0,get_screen().height()/2};
        center -= QPoint{0,get_rect().height()};
        move_center(center);
        set_interval(player_interval);
    }

    bool Player::enemy_collision(QList<Enemy>& enemys, QList<Bomb::Bomb>& bombs) const
    {
        for (auto i=enemys.begin(); i!=enemys.end(); ++i)
            if (Move_object::collision(*i)) {
                enemys.erase(i);
                Bomb::Bomb new_bomb {get_rect().center()};
                bombs.append(new_bomb);
                return true;
            }
        return false;
    }

    bool Enemy::update_pos(QList<Bullet>& bullets, QList<Bomb::Bomb>& bombs)
    {
        offset_move(get_speed());
        if (auto_destroy())
            return true;
        return destroy_collision(bullets, bombs);
    }

    void Enemy::shoot(QList<Bullet> &bullets)
    {
        if (interval()) {
            Bullet temp_bullet {bullet_enemy,-bullet_enm_spd,get_screen()};
            temp_bullet.move_center(get_rect().center());
            bullets.append(temp_bullet);
        }
    }

    void Enemy::init_pos()
    {
        set_pixmap(enemy_path.arg(rand_pix_num()));
        uniform_int_distribution<> interpret {0,get_screen().width()-get_rect().width()};
        move(QPoint{interpret(eng),-get_speed()-get_rect().height()});
        set_interval(enemy_interval);
    }

    void Boss::to_scene(int c_x)
    {
        center_x = c_x;
        init_pos();
        seen = true;
        stay = 0;
        light = 0;
    }

    void Boss::shoot(QList<Bullet>& bullets)
    {
        Bullet modal_bullet {bullet_boss,-bullet_boss_spd,get_screen()};
        for (QPoint bullet_pos{get_rect().bottomLeft()}; bullet_pos.x()<=get_rect().right(); bullet_pos+=QPoint{static_cast<int>(modal_bullet.get_rect().width()*bullet_boss_gap),0}) {
            Bullet new_bullet {modal_bullet};
            new_bullet.move_center(bullet_pos);
            bullets.append(new_bullet);
        }
    }

    bool Boss::update(QList<Bullet>& bullets, QList<Bullet>& boss_bullets, QList<Bomb::Bomb>& bombs)
    {
        if (!seen)
            return false;
        else if (health <= 0) {
            health = 0;
            Bomb::Bomb new_bomb {get_rect().center()};
            bombs.append(new_bomb);
            seen = false;
            return true;
        }
        else if (stay == 0)
            if (get_rect().y() < boss_pos)
                offset_move(-get_speed());
            else
                ++stay;
        else if (stay < boss_stay/2)
            ++stay;
        else if (stay == boss_stay/2) {
            if (light == 0) {
                std::thread th_beep {light_beep,boss_light_sound,boss_light*1000/frame_update};
                th_beep.detach();
            }
            if (++light == boss_light) {
                ++stay;
                shoot(boss_bullets);
            }
        }
        else if (stay == boss_stay)
            if (get_rect().y() > -get_rect().height())
                offset_move(get_speed());
            else {
                seen = false;
                return false;
            }
        else
            ++stay;
        for (auto i=bullets.begin(); i!=bullets.end(); )
            if (collision(*i)) {
                Bomb::Bomb new_bomb {(*i).get_rect().center()};
                bombs.append(new_bomb);
                i = bullets.erase(i);
                --health;
            }
            else
                ++i;
        return false;
    }

    void Boss::draw_this(QPixmap *pix) const
    {
        if (seen) {
            Move_object::draw_this(pix);
            if (stay == boss_stay/2) {
                QPainter painter {pix};
                painter.setBrush(QBrush{boss_light_c});
                painter.setPen(QPen{boss_light_c});
                painter.drawEllipse(get_rect().center()+QPoint{0,get_rect().height()/2}, boss_light_circle, boss_light_circle);
                painter.drawRect(light_rect());
            }
            draw_health(pix);
        }
    }

    bool Boss::beat_player(const Player* player, QList<Bomb::Bomb>& bombs) const
    {
        if (!seen)
            return false;
        bool beat {false};
        if (stay==boss_stay/2 and player->collision(light_rect()))
            beat = true;
        else
            beat = player->collision(get_rect());
        if (beat) {
            Bomb::Bomb new_bomb {player->get_rect().center()};
            bombs.append(new_bomb);
        }
        return beat;
    }

    void Boss::practice_update(QList<Bullet>& boss_bullets)
    {
        seen = true;
        move_center(QPoint{get_screen().center().x(), boss_pos});
        static int interval {};
        if (++interval == boss_shoot_interval) {
            shoot(boss_bullets);
            interval = 0;
        }
    }

    void Boss::init_pos()
    {
        move_center(QPoint{center_x,0});
        offset_move(get_rect().height()/2);
        offset_move(-get_speed());
    }

    void Boss::draw_health(QPixmap* pix) const
    {
        QPainter painter {pix};
        int max_health {(get_screen().topRight()-health_pos-health_pos).x()};
        QRect health_rect {health_pos,QSize{max_health,health_height}};
        painter.setBrush(QBrush{health_bgc});
        painter.drawRect(health_rect);
        health_rect.setWidth(max_health*health/boss_health);
        painter.setBrush(QBrush{health_color});
        painter.drawRect(health_rect);
    }

    QRect Boss::light_rect() const
    {
        QRect light_rect {QPoint{get_rect().center()-QPoint{boss_light_b/2,0}},QSize{boss_light_b,get_screen().height()}};
        light_rect.moveTop(get_rect().bottom());
        return light_rect;
    }

}
